﻿#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>



//匿名结构体类型
//struct
//{
//	char a;
//	int b;
//}s1;
//也就只能使用一次，在上面的s1就是
//因为没有名字，无法引用结构体

//这里是使用typedef对结构体进行重新定义
//typedef struct
//{
//	int data;
//	Node* next;
//}Node;
//错误用法，Node还未定义不可引用



//结构体的自引用
//struct Node
//{
//	int a;
//	//struct Node next;//这样写不是合理的，套娃似的，无穷个int型，运行会报错
//	struct Node* next;//这样写就合理了，因为这里储存的是地址(具有固定值)，具体存储规则下面讨论
//};
//int main()
//{
//	printf("%zd\n", sizeof(struct Node));//求struct Node结构体所占字节大小
//	return 0;
//}



//结构体内存对齐
//具体规则看课件
//struct s1
//{
//	char a;//从0开始储存一个字节(char类型占1个字节)
//	//0 1 2 3 ……中0被a占了 
//	char b;//找对其数，char是1 VS默认为8，1<8，对其数为1(对应类型所占字节与默认对其数比较，小的为对其数)
//	//找1的倍数，刚好1就是，b把1占了
//	int c;//找对其数，int是4，4<8，对其数为4
//	//找4的倍数，4就是，c把4 5 6 7占了(1个int型占用4个字节)
//
//	//综上，0被a占，1被b占，4 5 6 7被c占，但2 3空着
//	//然后结构体所占空间为最最大对其数(4)的整数倍，0-7刚好8
//};
//struct s2
//{
//	char a;//对其数1，a占0
//	int b;//对其数4，b占4 5 6 7
//	char c;//对其数1，c占8
//
//	//最大对其数4，0-11,12个字节
//};
//struct s3
//{
//	char a;//对其数1，a占0
//	double b;//(double型为8)对其数8，b占8-15(8为8的倍数)
//	int c;//对其数4，c占16-19(16为4的倍数)
//
//	//最大对其数8,0-23,24个字节
//};
//#pragma pack(1)//修改默认对其数为1(原先默认对其数为8)
//struct s4
//{
//	char a;
//	double b;
//	int c;
//	//因为对其数都为1，那么储存就是一个字节一个字节紧挨着排序了，结构体所占字节应为1+8+4=13
//};
//#pragma pack()//重置默认对齐数(也就是设置为原来的默认对其数8)
//int main()
//{
//	printf("%d\n", sizeof(char));
//	printf("%d\n", sizeof(int));
//	printf("%d\n", sizeof(short));
//	printf("%d\n\n", sizeof(double));
//	
//	printf("%d\n", sizeof(struct s1));
//	printf("%d\n", sizeof(struct s2));
//	printf("%d\n", sizeof(struct s3));
//	printf("%d\n", sizeof(struct s4));
//
//	return 0;
//}//之所以内存中这样对其，是处于性能的考虑(具体看课件)，总的来说就是用空间换取性能

 

//结构体传参
//struct S
//{
//	int data[1000];
//	int num;
//};
//struct S s = { {1,2,3,4}, 1000 };
////结构体传参
//void print1(struct S s)//可以将struct S看做一种类型(如int型)，后面的s为形参(如int x中的x)
//{
//	printf("%d\n", s.num);//.是用于结构体类型
//}
////结构体地址传参
//void print2(struct S* ps)
//{
//	printf("%d\n", ps->num);//->是用于结构体指针类型
//}
//int main()
//{
//	print1(s);//s为实参
//	print2(&s);
//	return 0;
//}
//地址传参比较好，具体原因如下：(毕竟传一个结构体的话，你不知道这个结构体有多大，可能会效率很慢)
//*函数传参的时候，参数是需要压栈，会有时间和空间上的系统开销。
//*如果传递⼀个结构体对象的时候，结构体过⼤，参数压栈的的系统开销⽐较⼤，所以会导致性能的下降。
//(详细一点，就是直接传入结构体，实际上就是再创建一个一样的结构体，但不是原来已经创建的结构体，是另一个结构体，地址不同)
//(而传入结构体地址，是直接使用前面已经创建的结构体，这就可以直接调用了，不费时也不费空间)



//结构体实现位段
//struct S
//{
//	//位段的单位是比特位，1字节(Byte)=8比特位(Bit)
//	char a : 3;//a=10，1010，3个比特位取010
//	char b : 4;//a=12，1100，4个比特位取1100
//	char c : 5;//a=3 ，0011，5个比特位取00011
//	char d : 4;//a=4 ，0100，4个比特位取0100
//};
//struct S s = { 0 };
//int main()
//{
//	s.a = 10;
//	//先拿一个字节00 00 00 00
//	//把a放进去   00 00 0*0 10*
//	s.b = 12;
//	//剩下5个比特位，足够放下b
//	//把b放进去   0*1 10 0*0 10*
//	s.c = 3;
//	//剩下1个比特位，不够放下c，再拿一个字节00 00 00 00
//	//把c放进去00 0*0 00 11*
//	s.d = 4;
//	//剩下3个比特位，不够放下d，再拿一个字节00 00 00 00
//	//把d放进去00 00 *01 00*
//	 
//	//那么一起就是0110 0010 0000 0011 0000 0100，地址显示62 03 04(16进制)，一共3个字节
//	printf("%d\n", sizeof(s));
//
//	return 0;
//}

//位段的使用一般是为了节省内存空间

//注意事项
//因为位段再有的情况下几个数据占用一个字节，就导致了要对一个数据(这个数据在一个字节中间)进行赋值没有地址
//所以一般是先将值赋给一个变量，再让数据等于这个变量，举例如下：
struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
int main()
{
	struct A sa = { 0 };
	//scanf("%d", &sa._b);//这是错误的

	//正确的⽰范
	int b = 0;
	scanf("%d", &b);
	sa._b = b;
	return 0;
}